package com.sinochem.yunlian.ship.common.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sinochem.yunlian.ship.common.enums.ResponseCode;
import com.sinochem.yunlian.ship.common.exception.BaseException;

/**
 * 通用校验类，JSON转MAP
 * 
 * @author cyp
 *
 */
public class ParamMap {

    private Logger LOG = LoggerFactory.getLogger(getClass());

    private Map<String, Object> mapParam = null;

    private String[] ARRAY_DATETIME_FORMAT = new String[]{"yyyy-MM", "yyyyMM", "yyyy/MM",
            "yyyyMMdd", "yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMddHHmmss",
            "yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss"};

    public ParamMap() {
    }

    public ParamMap(String data) {
        try {
            this.mapParam = JSONObject.parseObject(data, Map.class);
        } catch (Throwable e) {
            throw new BaseException("数据格式有误,解析失败,请检查数据格式和参数类型!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    public ParamMap(Map data) {
        this.mapParam = data;
    }

    public ParamMap setData(String data) {
        try {
            this.mapParam = JSONObject.parseObject(data, Map.class);
        } catch (Throwable e) {
            throw new BaseException("数据格式有误,解析失败,请检查数据格式和参数类型!",ResponseCode.INVALID_PARAM.getCode());
        }
        return this;
    }

    public String getString(String key) {
        if (mapParam == null) {
            throw new BaseException("无数据来源",ResponseCode.INVALID_PARAM.getCode());
        } else if (mapParam.get(key) == null) {
            return null;
        }
        return mapParam.get(key).toString();
    }

    public Map<String, Object> getMap() {
        return mapParam;
    }

    public Integer getInteger(String key) {
        checkValue(key);
        if (isParamBlank(key)) {
            return null;
        }
        String num = mapParam.get(key).toString();
        if (!NumberUtils.isNumber(num)) {
            throw new BaseException("参数" + key + " 有误,只能输入数字!",ResponseCode.INVALID_PARAM.getCode());
        }
        try {
            return Integer.valueOf(num);
        } catch (Throwable e) {
            throw new BaseException("参数" + key + "转换成整数时失败!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    public Long getLong(String key) {
        checkValue(key);
        if (isParamBlank(key)) {
            return null;
        }
        String num = mapParam.get(key).toString();
        if (!NumberUtils.isNumber(num)) {
            throw new BaseException("参数" + key + " 有误,只能输入数字!",ResponseCode.INVALID_PARAM.getCode());
        }
        try {
            return Long.valueOf(num);
        } catch (Throwable e) {
            throw new BaseException("参数" + key + "转换成整数时失败!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    public BigDecimal getBigDecimal(String key) {
        checkValue(key);
        if (isParamBlank(key)) {
            return null;
        }
        String num = mapParam.get(key).toString();
        if (!NumberUtils.isNumber(num)) {
            throw new BaseException("参数" + key + " 有误,只能输入数字!",ResponseCode.INVALID_PARAM.getCode());
        }
        try {
            return new BigDecimal(num);
        } catch (Throwable e) {
            throw new BaseException("参数" + key + "转换数字失败!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    public Double getDouble(String key) {
        checkValue(key);
        if (isParamBlank(key)) {
            return null;
        }
        String num = mapParam.get(key).toString();
        if (!NumberUtils.isNumber(num)) {
            throw new BaseException("参数" + key + " 有误,只能输入数字!",ResponseCode.INVALID_PARAM.getCode());
        }
        try {
            return Double.valueOf(num);
        } catch (Throwable e) {
            throw new BaseException("参数" + key + "转换数字失败!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    public Date getDate(String key) {
        checkValue(key);
        if (isParamBlank(key)) {
            return null;
        }
        try {
            return org.apache.commons.lang3.time.DateUtils.parseDate(mapParam.get(key).toString(), ARRAY_DATETIME_FORMAT);
        } catch (Throwable e) {
            throw new BaseException("参数" + key + "格式有误, 不能转换成日期!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    public void put(String key, Object value) {
        if(mapParam == null) {
            mapParam = new HashMap();
        }
        mapParam.put(key, value);
    }

    public <T> T getObject(String key, Class<T> clazz) {
        try {
            if (isParamBlank(key)) {
                return null;
            }
            return JSON.parseObject(mapParam.get(key).toString(), clazz);
        } catch (Throwable e) {
            throw new BaseException("数据有误,对象转换失败,请检查数据格式和参数类型!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    /**
     * 用paramMap中的值生成一个指定类型的实体
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T convertToObject(Class<T> clazz) {
        try {
            if (null == mapParam) {
                return null;
            }
            return JSON.parseObject(JSON.toJSONString(mapParam), clazz);
        } catch (Throwable e) {
            throw new BaseException("数据有误,对象转换失败,请检查数据格式和参数类型!",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    /**
     * 将paramMap中相同字段的值复制到bean中(注意: 若bean中字段有值,此方法会覆盖原值)
     * @param bean
     */
    public void copyFieldsToBean(Object bean){
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                for (Map.Entry<String, Object> entry : mapParam.entrySet()) {
                    if(field.getName().equals(entry.getKey()) && null != entry.getValue()) {
                        if (field.getGenericType().toString().equals("class java.util.Date")) {
                            field.set(bean, this.getDate(entry.getKey()));
                        } else if (field.getGenericType().toString().equals("class java.lang.Double")) {
                            field.set(bean, this.getDouble(entry.getKey()));
                        } else if (field.getGenericType().toString().equals("class java.lang.Integer")) {
                            field.set(bean, this.getInteger(entry.getKey()));
                        } else if (field.getGenericType().toString().equals("class java.lang.String")) {
                            field.set(bean, this.getString(entry.getKey()));
                        } else {
                            field.set(bean, entry.getValue());
                        }
                    }
                }
            } catch (Exception e) {
                LOG.info("该属性:"+ field.getName() + "复制值时出错, 所以跳过改属性复制. 错误原因:" + e.getMessage());
            }
        }
    }

    public ParamList getParamList(String key) {
        if (isParamBlank(key)) {
            return new ParamList();
        }
        return new ParamList(mapParam.get(key).toString());
    }

    public ParamMap getParamMap(String key) {
        if (isParamBlank(key)) {
            return new ParamMap();
        }
        return new ParamMap(mapParam.get(key).toString());
    }

    public ParamMap checkNull(String... params) {
        for (String param : params) {
            if (null == mapParam.get(param)) {
                throw new BaseException("参数"+param+"不能为空!",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    public ParamMap checkEmpty(String... params) {
        for (String param : params) {
            if (null == mapParam.get(param) || "".equals(mapParam.get(param).toString())) {
                throw new BaseException("参数" + param + "不能为空或空字符串! ",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    public ParamMap checkMinLength(int minLength, String... params) {
        if (minLength < 0) {
            throw new BaseException("校验出错,最小长度限制不能小于0",ResponseCode.INVALID_PARAM.getCode());
        }
        for (String param : params) {
            int len = mapParam.get(param).toString().length();
            if (null == mapParam.get(param) || len < minLength) {
                throw new BaseException("参数 " + param + " 长度必须大于或等于 " + minLength + " !",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    public ParamMap checkMaxLength(int maxLength, String... params) {
        if (maxLength <= 0) {
            throw new BaseException("校验出错,最大长度限制不能小于或等于0",ResponseCode.INVALID_PARAM.getCode());
        }
        for (String param : params) {
            if (mapParam.get(param) == null) {
                continue;
            }
            int len = mapParam.get(param).toString().length();
            if (null == mapParam.get(param) || len > maxLength) {
                throw new BaseException("参数 " +param + " 长度必须小于或等于 " + maxLength +" !",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    public ParamMap checkMaximum(double maxValue, String... params) {
        for (String param : params) {
            if (isParamBlank(param)) {
                continue;
            }
            if (!NumberUtils.isNumber(mapParam.get(param).toString())) {
                throw new BaseException("参数 " + param + " 必须为数字!",ResponseCode.INVALID_PARAM.getCode());
            }
            Double number = this.getDouble(param);
            if (number > maxValue) {
                DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
                df1.setGroupingUsed(false);
                throw new BaseException("参数 " + param + " 值必须小于或等于 " + df1.format(maxValue) + " !",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    public ParamMap checkMinimum(double minValue, String... params) {
        for (String param : params) {
            if (isParamBlank(param)) {
                continue;
            }
            if (!NumberUtils.isNumber(mapParam.get(param).toString())) {
                throw new BaseException("参数 " + param + " 必须为数字!",ResponseCode.INVALID_PARAM.getCode());
            }
            Double number = this.getDouble(param);
            if (number < minValue) {
                DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
                df1.setGroupingUsed(false);
                throw new BaseException("参数 " + param + " 值必须大于或等于 " + df1.format(minValue) + " !",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    /**
     * 校验Date格式, 仅支持yyyy-MM-dd格式的校验
     * @param params
     * @return
     */
    public ParamMap checkDate(String... params) {
        Pattern datePattern = Pattern.compile("^((\\d{2}(([02468][048])|([13579][26]))\\-((((0?[13578])|(1[02]))\\-((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))\\-((0?[1-9])|([1-2][0-9])|(30)))|(0?2\\-((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))\\-((((0?[13578])|(1[02]))\\-((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))\\-((0?[1-9])|([1-2][0-9])|(30)))|(0?2\\-((0?[1-9])|(1[0-9])|(2[0-8]))))))$");
        for (String param : params) {
            if (isParamBlank(param)) {
                continue;
            }
            if (!datePattern.matcher(this.getString(param)).matches()) {
                throw new BaseException("参数"+param+"格式不正确",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    /**
     * 校验DateTime格式, 仅支持yyyy-MM-dd HH:mm:ss格式的校验
     * @param params
     * @return
     */
    public  ParamMap checkDateTime(String... params) {
        Pattern dateTimePattern = Pattern.compile("^((\\d{2}(([02468][048])|([13579][26]))\\-((((0?[13578])|(1[02]))\\-((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))\\-((0?[1-9])|([1-2][0-9])|(30)))|(0?2\\-((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))\\-((((0?[13578])|(1[02]))\\-((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))\\-((0?[1-9])|([1-2][0-9])|(30)))|(0?2\\-((0?[1-9])|(1[0-9])|(2[0-8]))))))(\\s(((0[0-9])|([1][0-9])|([2][0-3]))\\:([0-5][0-9])((\\s)|(\\:([0-5][0-9])))))$");
        for (String param : params) {
            if (isParamBlank(param)) {
                continue;
            }
            if (!dateTimePattern.matcher(this.getString(param)).matches()) {
                throw new BaseException("参数"+param+"格式不正确",ResponseCode.INVALID_PARAM.getCode());
            }
        }
        return this;
    }

    /**
     * 校验boolean类型的值是否正确, 并填充默认值
     * @param key
     * @param trueValue
     * @param falseValue
     * @return
     */
    public ParamMap checkBoolean(String key, String trueValue, String falseValue) {
        if(isParamBlank(key)) {
            return this;
        }
        String value = this.getString(key);
        if (!value.equals(trueValue) && !value.equals(falseValue)){
            throw new BaseException(key + "参数值不合法!",ResponseCode.INVALID_PARAM.getCode());
        }
        return this;
    }
    
    @Override
    public String toString() {
        return JSON.toJSONString(mapParam);
    }

    /**
     *  判断此map是否为空
     */
    public boolean isEmpty() {
        return mapParam == null ? true : mapParam.isEmpty();
    }

    /**
     *  判断此map是否不为空
     */
    public boolean isNotEmpty() {
        return !this.isEmpty();
    }

    public ParamMap setDefaultValue(String key, Object defaultValue) {
        if(isParamBlank(key)) {
            mapParam.put(key, defaultValue);
        }
        return this;
    }

    private void checkValue(String key) {
        if (mapParam == null /*|| mapParam.get(key) == null*/) {
            throw new BaseException("无数据来源",ResponseCode.INVALID_PARAM.getCode());
        }
    }

    private boolean isParamBlank(String key) {
        return null == mapParam.get(key) || StringUtils.isBlank(mapParam.get(key).toString());
    }
    
    public ParamMap setParamList(String key, List paramList) {
        if(mapParam != null) {
           mapParam.put(key, paramList);
        }
        return this;
    }
    
    public ParamMap setParamMap(String key, ParamMap paramMap) {
        if(mapParam != null) {
           mapParam.put(key, paramMap);
        }
        return this;
    }

    /**
     * Bean转Map
     * @param bean
     * @return
     */
    public static Map<String,Object> convertBeanToMap(Object bean) {
        Class type = bean.getClass();
        Map<String,Object> returnMap = new HashMap<String,Object>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                if (!propertyName.equals("class")) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(bean, new Object[0]);
                    if (result != null) {
                        returnMap.put(propertyName, result.toString());
                    } else {
                        returnMap.put(propertyName, "");
                    }
                }
            }
        }catch (Exception e){
            throw new BaseException("Bean转Map失败!",ResponseCode.MESSAGE_NOET_READABLE.getCode());
        }
        return returnMap;
    }

    public static void main(String[] arrs) {
//        String data = "{\"deliverOrderType\":\"XSCK\",\"orderCode\":\"LBF0000088\",\"orderCodeWms\":\"HYtest020700090\",\"orderCode2\":\"1111111\",\"preDeliveryOrderCode\":\"precode\",\"preDeliveryOrderCodeWMS\":\"preDeliveryOrderCodeWMS\",\"projectCode\":\"zc0000100001\",\"ownerCode\":\"WM2000002\",\"warehouseCode\":\"warehouseCode\",\"orderTime\":\"2017-02-08 11:22:01\",\"operTimeERP\":\"2017/02/10 11:22:22\",\"payTime\":\"2017-02-10 11:22:23\",\"deliveryTime\":\"2017-02-10 11:22:24\",\"earliestDeliveryTime\":\"2017-02-10 11:22:25\",\"latestDeliveryTime\":\"2017-02-10 11:22:26\",\"consigneeCode\":\"consigneeCode\",\"logisticsCode\":\"SF\",\"logisticsName\":\"logisticsName\",\"expressCode\":\"expressCode\",\"pickingAddress\":\"江苏省盐城市大佛禅寺\",\"earliestPickingTime\":\"2017-02-08 11:22:01\",\"latestPickingTime\":\"2017-02-08 11:22:01\",\"sales\":\"sales\",\"saleTel\":\"12345678901\",\"callCenterTel\":\"12345678901\",\"paymentMethod\":\"ONLINE\",\"payStatus\":\"Y\",\"totalAmount\":\"9999999\",\"amount\":\"88888888\",\"gotAmount\":\"2222222\",\"itemAmount\":\"33333333333\",\"discountAmount\":\"111111\",\"freight\":\"123123\",\"serviceFee\":\"999\",\"isUrgency\":\"Y\",\"isInvoiceFlag\":\"Y\",\"remark\":\"testremark\",\"getInfo\":{\"consignee\":\"李晓娜\",\"consigneeOrg\":\"consigneeOrg\",\"consigneePhone\":\"12345678901\",\"deliveryCity\":\"上海市\",\"deliveryDistrict\":\"青浦区\",\"deliveryProvince\":\"上海市\",\"deliveryVillage\":\"\",\"deliveryAddress\":\"青浦大观园\"},\"sendInfo\":{\"forwardingOrg\":\"forwardingOrg\",\"shipper\":\"货主测试1\",\"sPhone\":\"13456787665\"},\"cargo\":[{\"orderLineNo\":\"orderLineNo7\",\"itemCode\":\"itemCode\",\"name\":\"name\",\"type\":\"type\",\"unit\":\"unit\",\"quantity\":\"23\",\"inventoryType\":\"ZP\",\"retailPrice\":\"11\",\"prodPrice\":\"22\",\"actualPrice\":\"33\",\"balancePrice\":\"44\",\"discountAmount\":\"55\",\"productDate\":\"2017-02-12 \",\"expireDate\":\"2017-02-12\",\"produceCode\":\"produceCode\",\"batchCode\":\"batchCode\"},{\"orderLineNo\":\"orderLineNo72\",\"itemCode\":\"itemCode\",\"name\":\"name\",\"type\":\"type\",\"unit\":\"unit\",\"quantity\":\"23\",\"inventoryType\":\"CC\",\"retailPrice\":\"11\",\"prodPrice\":\"22\",\"actualPrice\":\"33\",\"balancePrice\":\"44\",\"discountAmount\":\"55\",\"productDate\":\"2017-02-12\",\"expireDate\":\"2017-02-12\",\"produceCode\":\"produceCode\",\"batchCode\":\"batchCode\"}],\"invoices\":[{\"invoiceType\":\"INVOICE\",\"invoiceHeader\":\"invoiceHeade7r1\",\"invoiceAmount\":\"9999999\",\"invoiceContent\":\"invoiceContent\",\"invoiceDetail\":[{\"itemName\":\"itemName71\",\"unit\":\"unit\",\"price\":\"12311\",\"quantity\":\"888888\",\"amount\":\"123123.123\"},{\"itemName\":\"itemNam72e\",\"unit\":\"unit\",\"price\":\"12311\",\"quantity\":\"888888\",\"amount\":\"123123.123\"}]},{\"invoiceType\":\"INVOICE\",\"invoiceHeader\":\"invoiceHeader72\",\"invoiceAmount\":\"9999999\",\"invoiceContent\":\"invoiceContent\",\"invoiceDetail\":[{\"itemName\":\"itemName7\",\"unit\":\"unit\",\"price\":\"12311\",\"quantity\":\"888888\",\"amount\":\"123123.123\"}]}]}";
//        ParamMap params = new ParamMap(data);
//        params.checkEmpty("orderCode", "orderCode2")
//        .checkMaxLength(1, "orderCode")
//        .checkMaxLength(50, "orderCode2");
//        params.checkDateTime("orderTime");
//        System.out.println(123);
//        TestBean testBean = new TestBean();
//        testBean.setPassword("1234");
//        testBean.setUserName("233232");
//        ParamMap paramMap = new ParamMap(ParamMap.convertBeanToMap(testBean));
//        System.out.println(JSON.toJSON(paramMap.getMap()));
    }

}
